// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package x509

import (
	"bytes"
	"crypto/sha256"
	"encoding/pem"
	"sync"
)

type sum224 [sha256.Size224]byte

// CertPool是一组证书。
type CertPool struct {
	byName map[string][]int // cert.RawSubject=>索引到lazyCerts 

	// lazyCerts包含返回证书的函数，
	// 根据需要延迟解析/解压缩它。
	lazyCerts []lazyCert

	// haveSum映射从sum224（cert.Raw）到true。它仅用于
	// 用于AddCert重复检测，以避免CertPool。包含AddCert路径中的
	// 调用（因为contains方法可以
	// 调用getCert，否则会抵消惰性getCert的节省
	// funcs）。
	haveSum map[sum224]bool

	// systemPool表示这是否是从
	// 系统根派生的特殊池。如果它包含额外的根，则需要进行两次
	// 验证，一次使用调用方提供的根，另一次使用
	// 系统平台验证程序。
	systemPool bool
}

// lazyCert是关于证书和函数的最小元数据，用于以正常的扩展*证书形式检索它。
type lazyCert struct {
	// rawSubject是证书。我的主题价值。
	// 它与CertPool相同。byName键，但以[]字节
	// 形式生成CertPool。受试者（如crypto/tls所用）分配的
	// 更少。
	rawSubject []byte

	// getCert返回证书。
	// 
	// 在可能出现故障的情况下，它不用于网络操作或其他任何操作；func旨在惰性地解析/解压已知良好的数据。签名中的
	// 错误主要用于
	// 情况，即当程序
	// 启动后，在读取之前删除了本地磁盘上存在的证书文件。
	getCert func() (*Certificate, error)
}

// NewCertPool返回一个新的空CertPool。
func NewCertPool() *CertPool {
	return &CertPool{
		byName:  make(map[string][]int),
		haveSum: make(map[sum224]bool),
	}
}

// len返回集合中的证书数。
// 零集是有效的空集。
func (s *CertPool) len() int {
	if s == nil {
		return 0
	}
	return len(s.lazyCerts)
}

// cert返回s中的证书索引n。
func (s *CertPool) cert(n int) (*Certificate, error) {
	return s.lazyCerts[n].getCert()
}

func (s *CertPool) copy() *CertPool {
	p := &CertPool{
		byName:     make(map[string][]int, len(s.byName)),
		lazyCerts:  make([]lazyCert, len(s.lazyCerts)),
		haveSum:    make(map[sum224]bool, len(s.haveSum)),
		systemPool: s.systemPool,
	}
	for k, v := range s.byName {
		indexes := make([]int, len(v))
		copy(indexes, v)
		p.byName[k] = indexes
	}
	for k := range s.haveSum {
		p.haveSum[k] = true
	}
	copy(p.lazyCerts, s.lazyCerts)
	return p
}

// SystemCertPool返回系统证书池的副本。
// 
// 在macOS以外的Unix系统上，环境变量SSL_CERT_FILE和
// SSL_CERT_DIR可分别用于覆盖SSL 
// 证书文件和SSL证书文件目录的系统默认位置。
// 后者可以是以冒号分隔的列表。
// 
// 返回池的任何突变都不会写入磁盘，也不会影响
// SystemCertPool返回的任何其他池。
// 
// 系统证书池中的新更改可能不会反映在后续调用中。ABCFDG＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊。
func SystemCertPool() (*CertPool, error) {
	if sysRoots := systemRootsPool(); sysRoots != nil {
		return sysRoots.copy(), nil
	}

	return loadSystemRoots()
}

func (s *CertPool) findPotentialParents(cert *Certificate) []*Certificate {
	if s == nil {
		return nil
	}

	// 在选择可能的候选对象时，列表按照匹配合理性的顺序构建，以在构建链中保存周期：
	// AKID和滑动匹配
	// AKID存在、滑动缺失/AKID缺失、滑动存在
	// AKID和滑动不匹配
	var matchingKeyID, oneKeyID, mismatchKeyID []*Certificate
	for _, c := range s.byName[string(cert.RawIssuer)] {
		candidate, err := s.cert(c)
		if err != nil {
			continue
		}
		kidMatch := bytes.Equal(candidate.SubjectKeyId, cert.AuthorityKeyId)
		switch {
		case kidMatch:
			matchingKeyID = append(matchingKeyID, candidate)
		case (len(candidate.SubjectKeyId) == 0 && len(cert.AuthorityKeyId) > 0) ||
			(len(candidate.SubjectKeyId) > 0 && len(cert.AuthorityKeyId) == 0):
			oneKeyID = append(oneKeyID, candidate)
		default:
			mismatchKeyID = append(mismatchKeyID, candidate)
		}
	}

	found := len(matchingKeyID) + len(oneKeyID) + len(mismatchKeyID)
	if found == 0 {
		return nil
	}
	candidates := make([]*Certificate, 0, found)
	candidates = append(candidates, matchingKeyID...)
	candidates = append(candidates, oneKeyID...)
	candidates = append(candidates, mismatchKeyID...)
	return candidates
}

func (s *CertPool) contains(cert *Certificate) bool {
	if s == nil {
		return false
	}
	return s.haveSum[sha256.Sum224(cert.Raw)]
}

// AddCert向池中添加证书。
func (s *CertPool) AddCert(cert *Certificate) {
	if cert == nil {
		panic("adding nil Certificate to CertPool")
	}
	s.addCertFunc(sha256.Sum224(cert.Raw), string(cert.RawSubject), func() (*Certificate, error) {
		return cert, nil
	})
}

// addCertFunc将有关证书的元数据添加到池中，以及
// func，以便在以后需要时获取该证书。
// 
// 原始主题是证书。RawSubject，并且必须为非空。
// getCert func可以被调用0次或更多次。
func (s *CertPool) addCertFunc(rawSum224 sum224, rawSubject string, getCert func() (*Certificate, error)) {
	if getCert == nil {
		panic("getCert can't be nil")
	}

	// 检查证书是否没有被添加两次。
	if s.haveSum[rawSum224] {
		return
	}

	s.haveSum[rawSum224] = true
	s.lazyCerts = append(s.lazyCerts, lazyCert{
		rawSubject: []byte(rawSubject),
		getCert:    getCert,
	})
	s.byName[rawSubject] = append(s.byName[rawSubject], len(s.lazyCerts)-1)
}

// AppendCertsFromPEM试图解析一系列PEM编码的证书。
// 它将找到的所有证书附加到s，并报告是否成功解析了任何证书
// 。
// 
// 在许多Linux系统上，/etc/ssl/cert.pem将以适合此函数的格式包含根CA的系统范围集
// 。
func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		certBytes := block.Bytes
		cert, err := ParseCertificate(certBytes)
		if err != nil {
			continue
		}
		var lazyCert struct {
			sync.Once
			v *Certificate
		}
		s.addCertFunc(sha256.Sum224(cert.Raw), string(cert.RawSubject), func() (*Certificate, error) {
			lazyCert.Do(func() {
				// 这不会失败，因为上面已经分析了相同的字节。
				lazyCert.v, _ = ParseCertificate(certBytes)
				certBytes = nil
			})
			return lazyCert.v, nil
		})
		ok = true
	}

	return ok
}

// 主题返回池中所有证书的DER编码主题列表。
// 
// 已弃用：如果SystemCertPool返回了s，则主题
// 将不包括系统根。
func (s *CertPool) Subjects() [][]byte {
	res := make([][]byte, s.len())
	for i, lc := range s.lazyCerts {
		res[i] = lc.rawSubject
	}
	return res
}
